package sysytmRepo

import (
	"gorm.io/gorm"
	"ruoyi/domain/entity/common"
	"ruoyi/domain/entity/response"
	"ruoyi/domain/entity/system"
	"ruoyi/domain/repository"
	"strconv"
	"strings"
)

type SysMenuRepo struct {
	db *gorm.DB
}

var curr *gorm.DB

func SysMenuRepository(db *gorm.DB) *SysMenuRepo {
	curr = db
	return &SysMenuRepo{db}

}

var _ repository.SysMenuRepository = &SysMenuRepo{}

func (r *SysMenuRepo) SelectMenuTreeByUserId(userId int) []system.SysMenu {
	var menu []system.SysMenu
	//if IsAdminById(userId) {
	if true {
		err := r.db.Where("menu_type in (?) ", []string{"M", "C"}).Where("status", "0").Order("parent_id").Order("order_num").Find(&menu).Error
		if err != nil {
			panic(response.ReturnFailMsg("查询错误"))
		}
	} else {
		err := r.db.Raw("select distinct m.* from sys_menu m left join sys_role_menu rm on m.menu_id = rm.menu_id left join sys_user_role ur on rm.role_id = ur.role_id left join sys_role ro on ur.role_id = ro.role_id left join sys_user u on ur.user_id = u.user_id where u.user_id = ? and m.menu_type in ('M', 'C') and m.status = 0  AND ro.status = 0 order by m.parent_id, m.order_num", userId).Scan(&menu).Error
		if err != nil {
			panic(response.ReturnFailMsg("查询错误"))
		}
	}
	return menu
}

func (r *SysMenuRepo) SelectMenuTree(userId int, menu system.SysMenu) []system.SysMenu {
	var menus []system.SysMenu
	sql := "select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time "
	sql += "from sys_menu m "
	//if IsAdminById(userId) {
	if true {
		var menuName = menu.MenuName
		if menuName != "" {
			sql += "AND m.menu_name like concat('%', " + menuName + ", '%')"
		}
		var visible = menu.Visible
		if visible != "" {
			sql += "AND m.visible = " + visible
		}
		var status = menu.Status
		if status != "" {
			sql += "AND m.status = " + status
		}
		err := r.db.Raw(sql).Scan(&menus).Error
		if err != nil {
			panic(response.ReturnFailMsg(err.Error()))
		}
	} else {
		sql += "left join sys_role_menu rm on m.menu_id = rm.menu_id "
		sql += "left join sys_user_role ur on rm.role_id = ur.role_id "
		sql += "left join sys_role ro on ur.role_id = ro.role_id "
		sql += "where ur.user_id = " + strconv.Itoa(userId)
		var menuName = menu.MenuName
		if menuName != "" {
			sql += "AND m.menu_name like concat('%', " + menuName + ", '%')"
		}
		var visible = menu.Visible
		if visible != "" {
			sql += "AND m.visible = " + visible
		}
		var status = menu.Status
		if status != "" {
			sql += "AND m.status = " + status
		}
		err := r.db.Raw(sql).Scan(&menus).Error
		if err != nil {
			panic(response.ReturnFailMsg(err.Error()))
		}
	}
	return menus
}

func (r *SysMenuRepo) BuildMenuTreeSelect(lists []system.SysMenu) []system.MenuTreeSelect {
	var menuTreeSelect []system.MenuTreeSelect
	for i := 0; i < len(lists); i++ {
		var menu = lists[i]
		menuId := menu.MenuId
		parentId := menu.ParentId
		if 0 == parentId {
			var menuVo = system.MenuTreeSelect{
				Id:    menuId,
				Label: menu.MenuName,
			}
			menuVo.Children = r.BuildChildMenusTreeSelect(menuId, lists)
			menuTreeSelect = append(menuTreeSelect, menuVo)
		}
	}
	return menuTreeSelect
}

func (r *SysMenuRepo) BuildChildMenusTreeSelect(ParentId int, lists []system.SysMenu) []system.MenuTreeSelect {
	var List []system.MenuTreeSelect
	for i := 0; i < len(lists); i++ {
		var menu = lists[i]
		var menuId = menu.MenuId
		var pId = menu.ParentId
		if pId == ParentId {
			var menuVo = system.MenuTreeSelect{
				Id:    menuId,
				Label: menu.MenuName,
			}
			menuVo.Children = r.BuildChildMenusTreeSelect(menuId, lists)
			List = append(List, menuVo)
		}
	}
	return List
}

func (r *SysMenuRepo) BuildMenus(lists []system.SysMenu) []system.MenuVo {
	var menuVos []system.MenuVo
	for i := 0; i < len(lists); i++ {
		var menu = lists[i]
		MenuId := menu.MenuId
		parentId := menu.ParentId
		if 0 == parentId {
			var path = ""
			if isInnerLink(menu.Path) {
				path = menu.Path
			}
			var menuVo = system.MenuVo{
				Hidden: "1" == menu.Visible,
				Query:  menu.Query,
				MetaVo: system.MetaVo{
					Title:   menu.MenuName,
					Icon:    menu.Icon,
					NoCache: "1" == menu.IsCache,
					Link:    path,
				},
				Name:      getRouteName(menu),
				Path:      getRouterPath(menu),
				Component: getComponent(menu),
			}
			if "M" == menu.MenuType {
				if !isInnerLink(menu.Path) {
					menuVo.AlwaysShow = true
					menuVo.Redirect = "noRedirect"
					menuVo.Children = r.BuildChildMenus(MenuId, lists)
				}
			}
			menuVos = append(menuVos, menuVo)
		}

	}
	return menuVos
}

func (r *SysMenuRepo) BuildChildMenus(ParentId int, lists []system.SysMenu) []system.MenuVo {
	var List []system.MenuVo
	for i := 0; i < len(lists); i++ {
		var menu = lists[i]
		var menuId = menu.MenuId
		var pId = menu.ParentId
		if pId == ParentId {
			var path = ""
			if isInnerLink(menu.Path) {
				path = menu.Path
			}
			var menuVo = system.MenuVo{
				Hidden: "1" == menu.Visible,
				Query:  menu.Query,
				MetaVo: system.MetaVo{
					Title:   menu.MenuName,
					Icon:    menu.Icon,
					NoCache: "1" == menu.IsCache,
					Link:    path,
				},
				Name:      getRouteName(menu),
				Path:      getRouterPath(menu),
				Component: getComponent(menu),
			}
			if "M" == menu.MenuType {
				if !isInnerLink(menu.Path) {
					menuVo.AlwaysShow = true
					menuVo.Redirect = "noRedirect"
					menuVo.Children = r.BuildChildMenus(menuId, lists)
				}
			}
			List = append(List, menuVo)
		}
	}
	return List
}

func getRouteName(menu system.SysMenu) string {
	var name = FirstUpper(menu.Path)
	if isMenuFrame(menu) {
		return ""
	}
	return name
}

func getRouterPath(menu system.SysMenu) string {
	var routerPath = menu.Path
	if isInnerLink(routerPath) {
		return routerPath
	}
	// 非外链并且是一级目录（类型为目录）
	if 0 == menu.ParentId && "M" == menu.MenuType && "1" == menu.IsFrame {
		routerPath = "/" + menu.Path
	} else if isMenuFrame(menu) {
		routerPath = "/"
	}
	return routerPath
}

func getComponent(menu system.SysMenu) string {
	var component = "Layout"
	if "" != menu.Component && !isMenuFrame(menu) {
		component = menu.Component
	} else if "" == menu.Component && isInnerLink(menu.Path) {
		component = "InnerLink"
	} else if "" == menu.Component && isParentView(menu) {
		component = "ParentView"
	}
	return component
}

func isParentView(menu system.SysMenu) bool {
	return menu.ParentId != 0 && "M" == menu.MenuType
}

// 是否为外链
func isInnerLink(path string) bool {
	return strings.Contains(path, "http://") || strings.Contains(path, "https://")
}

func isMenuFrame(menu system.SysMenu) bool {
	return menu.ParentId == 0 && "C" == menu.MenuType && menu.IsFrame == "1"
}

/*首字母大写*/
func FirstUpper(s string) string {
	if s == "" {
		return ""
	}
	return strings.ToUpper(s[:1]) + s[1:]
}

// SelectSysMenuList /*分页查询*/
func (r *SysMenuRepo) SelectSysMenuList(params common.SearchTableDataParam) []system.SysMenu {
	sysMenu := params.Other.(system.SysMenu)
	var rows []system.SysMenu
	var sql = "select menu_id, menu_name, parent_id, order_num, path, component, `query`, is_frame, is_cache, menu_type, visible, " +
		"status, ifnull(perms,'') as perms, icon, create_time from sys_menu where 1=1"
	var name = sysMenu.MenuName
	if name != "" {
		sql += "AND menu_name like concat(%" + name + "%)"
	}
	var visible = sysMenu.Visible
	if visible != "" {
		sql += "AND visible = " + visible
	}
	var status = sysMenu.Status
	if status != "" {
		sql += "AND status = " + status
	}
	err := r.db.Raw(sql).Find(&rows).Error
	if err != nil {
		panic(response.ReturnFailMsg(err.Error()))
	}
	return rows
}

func (r *SysMenuRepo) SelectMenuListByRoleId(roleId string, menuCheckStrictly bool) []int {
	var sql = "select m.menu_id from sys_menu m " +
		"left join sys_role_menu rm on m.menu_id = rm.menu_id " +
		"where rm.role_id = " + roleId + " "
	if menuCheckStrictly {
		sql += "and m.menu_id not in (select m.parent_id from sys_menu m inner join sys_role_menu rm on m.menu_id = rm.menu_id and rm.role_id = " + roleId + ")"
	}
	sql += "order by m.parent_id, m.order_num"
	var menuIds []int
	err := r.db.Raw(sql).Scan(&menuIds).Error
	if err != nil {
		panic(response.ReturnFailMsg(err.Error()))
	}
	return menuIds
}

func (r *SysMenuRepo) SelectSysMenuListByUserId(userId int, params common.SearchTableDataParam) []system.SysMenu {
	sysMenu := params.Other.(system.SysMenu)
	var sql = "select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.visible," +
		" m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time " +
		"from sys_menu m left join sys_role_menu rm on m.menu_id = rm.menu_id " +
		"left join sys_user_role ur on rm.role_id = ur.role_id " +
		"left join sys_role ro on ur.role_id = ro.role_id "

	sql += "where ur.user_id = " + strconv.Itoa(userId) + " "
	var menuName = sysMenu.MenuName
	if menuName != "" {
		sql += "AND m.menu_name like concat(%" + menuName + "%) "
	}
	var visible = sysMenu.Visible

	if visible != "" {
		sql += "AND m.visible = " + visible + " "
	}
	var status = sysMenu.Status
	if status != "" {
		sql += "AND m.status = " + status + " "
	}

	sql += "order by m.parent_id, m.order_num"
	var list []system.SysMenu
	err := r.db.Raw(sql).Scan(&list).Error
	if err != nil {
		panic(response.ReturnFailMsg(err.Error()))
	}
	return list
}

func (r *SysMenuRepo) FindMenuInfoById(menuId string) system.SysMenu {
	sql := "select menu_id, menu_name, parent_id, order_num, path, " +
		"component, `query`, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time " +
		"from sys_menu "
	sql = sql + "where menu_id = " + menuId
	var list system.SysMenu
	err := r.db.Raw(sql).First(&list).Error
	if err != nil {
		panic(response.ReturnFailMsg(err.Error()))
	}
	return list
}

func hasChildByMenuId(menuId string) system.SysMenu {
	var menu system.SysMenu
	curr.Where("parent_id = ? ", menuId).First(&menu)
	return menu
}

func hasChildCountByMenuId(menuId string) int {
	var menuCount int
	err := curr.Where("parent_id = ? ", menuId).Scan(&menuCount).Error
	if err != nil {
		panic(response.ReturnFailMsg(err.Error()))
	}
	return menuCount
}

func checkMenuExistRole(menuId string) int {
	var menuCount int
	err := curr.Raw("select count(1) from sys_role_menu where menu_id = " + menuId).Scan(&menuCount).Error
	if err != nil {
		panic(response.ReturnFailMsg(err.Error()))
	}
	return menuCount
}

func (r *SysMenuRepo) DeleteMenu(menuIds string) response.Response {
	menu := hasChildByMenuId(menuIds)
	if menu.MenuId != 0 {
		return response.ReturnFailMsg("存在子菜单,不允许删除")
	}

	menuCount := checkMenuExistRole(menuIds)
	if menuCount > 0 {
		return response.ReturnFailMsg("菜单已分配,不允许删除")
	}

	err := r.db.Exec("delete from sys_menu where menu_id in (?) ", menuIds).Error
	if err == nil {
		response.ReturnFailMsg("删除部门关联用户失败")
	}
	return response.ReturnSuccess("操作成功")
}

func checkMenuNameUnique(parentId int, menuName string) int {
	var menuCount int
	err := curr.Raw("select count(1) from sys_menu where menu_name = "+menuName+" and parent_id = "+strconv.Itoa(parentId), &menuCount).Error
	if err != nil {
		panic(response.ReturnFailMsg(err.Error()))
	}
	return menuCount
}

func (r *SysMenuRepo) AddMenu(sysMenu system.SysMenu) response.Response {
	count := checkMenuNameUnique(sysMenu.ParentId, sysMenu.MenuName)
	if count > 0 {
		return response.ReturnFailMsg("新增菜单" + sysMenu.MenuName + "失败，菜单名称已存在")
	}
	menuPath := sysMenu.Path
	isFrame := sysMenu.IsFrame
	isPath := isInnerLink(menuPath)
	if isFrame == "true" && !isPath {
		return response.ReturnFailMsg("新增菜单" + sysMenu.MenuName + "失败，地址必须以http(s)://开头")
	}
	err := r.db.Create(&sysMenu).Error
	if err != nil {
		panic(response.ReturnFailMsg(err.Error()))
	}
	return response.ReturnSuccess("操作成功")

}

func (r *SysMenuRepo) UpdateMenu(sysMenu system.SysMenu) response.Response {
	mId := sysMenu.MenuId
	pId := sysMenu.ParentId
	if pId == mId {
		return response.ReturnFailMsg("修改菜单" + sysMenu.MenuName + "失败，上级菜单不能选择自己")
	}
	count := checkMenuNameUnique(pId, sysMenu.MenuName)
	if count > 0 {
		return response.ReturnFailMsg("修改菜单失败，菜单名称已存在" + sysMenu.MenuName + "失败，菜单名称已存在")
	}
	isFrame := sysMenu.IsFrame
	menuPath := sysMenu.Path
	isPath := isInnerLink(menuPath)
	if isFrame == "true" && !isPath {
		return response.ReturnFailMsg("修改菜单" + sysMenu.MenuName + "失败，地址必须以http(s)://开头")
	}
	err := r.db.Updates(&sysMenu).Error
	if err != nil {
		panic(response.ReturnFailMsg(err.Error()))
	}
	return response.ReturnSuccess("操作成功")
}
